
module.exports = function(RED) {
    "use strict";
    var exec = require('child_process').exec;
    var fs = require('fs');

    var gpioCommand = '/usr/local/bin/gpiohb';

    //if (!fs.existsSync("/sys/devices/soc0/machine")) { // unlikely if not on a Hummingboard
    //throw "Info : Ignoring Hummingboard specific node.";
    //}

    if (!fs.existsSync(gpioCommand)) { // gpio command not installed
        throw "Error : Can't find "+gpioCommand+" command for Hummingboard.";
    }

    // Map physical P1 pins to Gordon's Wiring-Pi Pins (as they should be V1/V2 tolerant)
    var pintable = {
        // Physical : WiringPi
        "11":"0",
        "12":"1",
        "13":"2",
        "15":"3",
        "16":"4",
        "18":"5",
        "22":"6",
        "7":"7",
        "3":"8",
        "5":"9",
        "24":"10",
        "26":"11",
        "19":"12",
        "21":"13",
        "23":"14",
        "8":"15",
        "10":"16",
        "27":"30",
        "28":"31",
        "29":"21",
        "31":"22",
        "32":"26",
        "33":"23",
        "35":"24",
        "36":"27",
        "37":"25",
        "38":"28",
        "40":"29"
    }
    var tablepin = {
        // WiringPi : Physical
        "0":"11",
        "1":"12",
        "2":"13",
        "3":"15",
        "4":"16",
        "5":"18",
        "6":"22",
        "7":"7",
        "8":"3",
        "9":"5",
        "10":"24",
        "11":"26",
        "12":"19",
        "13":"21",
        "14":"23",
        "15":"8",
        "16":"10",
        "30":"27",
        "31":"28",
        "21":"29",
        "22":"31",
        "26":"32",
        "23":"33",
        "24":"35",
        "27":"36",
        "25":"37",
        "28":"38",
        "29":"40"
    }

    function GPIOHBInNode(n) {
        RED.nodes.createNode(this,n);
        this.buttonState = -1;
        this.pin = pintable[n.pin];
        this.intype = n.intype;
        this.read = n.read || false;
        if (this.read) { this.buttonState = -2; }
        var node = this;

        var readit = function() {
            exec(gpioCommand+" read "+node.pin, function(err,stdout,stderr) {
                if (err) { node.error(err); }
                else {
                    exec(gpioCommand+" wfi "+node.pin + " both", function(err,stdo,stde) {
                        if (err) { node.error(err); }
                        else { readit(); }
                    });
                    if (node.buttonState !== Number(stdout)) {
                        var previousState = node.buttonState;
                        node.buttonState = Number(stdout);
                        if (previousState !== -1) {
                            var msg = {topic:"pi/"+tablepin[node.pin], payload:(node.buttonState === 0 ? 0 : 1)};
                            node.send(msg);
                        }
                    }
                }
            });
        }

        if (node.pin !== undefined) {
            exec(gpioCommand+" mode "+node.pin+" "+node.intype, function(err,stdout,stderr) {
                if (err) { node.error(err); }
                else {
                    readit();
                }
            });
        }
        else {
            node.error("Invalid GPIO pin: "+node.pin);
        }

        node.on("close", function() {
            clearInterval(node._interval);
            exec(gpioCommand+" close "+node.pin);
        });
    }

    function GPIOHBOutNode(n) {
        RED.nodes.createNode(this,n);
        this.pin = pintable[n.pin];
        this.set = n.set || false;
        this.level = n.level || 0;
        this.out = n.out || "out";
        var node = this;
        if (node.out === "pwm") { node.op = "pwm"; }
        else { node.op = "write"; }

        if (node.pin !== undefined) {
            exec(gpioCommand+" mode "+node.pin+" "+node.out, function(err,stdout,stderr) {
                if (err) { node.error(err); }
                else {
                    if (node.set && (node.out === "out")) {
                        exec(gpioCommand+" write "+node.pin+" "+node.level, function(err,stdout,stderr) {
                            if (err) { node.error(err); }
                        });
                    }
                    node.on("input", function(msg) {
                        if (msg.payload === "true") { msg.payload = true; }
                        if (msg.payload === "false") { msg.payload = false; }
                        var out = Number(msg.payload);
                        var limit = 1;
                        if (node.out === "pwm") { limit = 1023; }
                        if ((out >= 0) && (out <= limit)) {
                            exec(gpioCommand+" "+node.op+" "+node.pin+" "+out, function(err,stdout,stderr) {
                                if (err) { node.error(err); }
                            });
                        }
                        else { node.warn("Invalid input: "+out); }
                    });
                }
            });
        }
        else {
            node.error("Invalid GPIO pin: "+node.pin);
        }

        node.on("close", function() {
            exec(gpioCommand+" close "+node.pin);
        });
    }

    RED.nodes.registerType("hb-gpio in",GPIOHBInNode);
    RED.nodes.registerType("hb-gpio out",GPIOHBOutNode);
}
